using System;
using System.Collections.Generic;
using System.Linq;
using ServiceStack.Common;
using ServiceStack.Text;

namespace ServiceStack.Common.Tests.Models
{
	public interface IExternal
	{
		string ExternalUrn { get; set; }
	}

	public interface IExternalDeletable : IExternal
	{
		bool Delete { get; set; }
	}

	public interface IContent : IExternal
	{
		Guid Id { get; set; }
		string Urn { get; set; }

		DateTime CreatedDate { get; set; }
		DateTime ModifiedDate { get; set; }
	}

	public interface IContentDeletable : IContent
	{
		DateTime? DeletedDate { get; set; }
	}

	public class MergeList<T>
	{
		public bool Partial { get; set; }
		public List<T> Items { get; set; }

		public MergeList()
		{
		}

		public MergeList(IEnumerable<T> items)
		{
			this.Items = new List<T>(items);
		}

		public override string ToString()
		{
			return String.Format("{0} {1} {2}s", this.Partial ? "Partial" : "Full", this.Items.NullableCount(), typeof(T).Name);
		}
	}

	public class CostPoint
	{
		public string Campaign { get; set; }
		public DateTime StartDate { get; set; }
		public string CostCode { get; set; }
	}

	public enum ExplicitType
	{
		Unknown,
		NotExplicit,
		Explicit,
		Cleaned
	}

	public enum ReleaseType
	{
		Single,
		Album,
		Ep,
		BoxedSet
	}

	public class Batch
	{
		public Guid Id { get; set; }
		public string Name { get; set; }
		public string Path { get; set; }
		public string SupplierKeyName { get; set; }
		public string SupplierName { get; set; }
		public string DeliveryKeyName { get; set; }
		public long SequenceNumber { get; set; }

		public override string ToString()
		{
			return String.Format("{0} {1}", this.SupplierKeyName, this.Name);
		}
	}

	public class Participant
	{
		public string Name { get; set; }
		public string Role { get; set; }
		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.Role);
		}
	}

	public class ArtistUpdate : IExternal
	{
		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public DateTime? BirthDate { get; set; }
		public string BirthPlace { get; set; }

		public DateTime? DeathDate { get; set; }
		public string DeathPlace { get; set; }

		public string DecadesActive { get; set; }

		public string Biography { get; set; }
		public string BiographyAuthor { get; set; }

		public List<AssetId> AssetIds { get; set; }

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.ExternalRef);
		}
	}

	public class AssetUpdate : IExternal
	{
		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }

		public AssetType AssetType { get; set; }

		public string ExternalOwnerUrn { get; set; }

		public string FileName { get; set; }
		public string FileExtension { get; set; }
		public long FileSizeBytes { get; set; }

		public string MasterSha256Checksum { get; set; }
		public string Md5Checksum { get; set; }

		public int? DurationMs { get; set; }
		public int? BitRateKbps { get; set; }

		public int? Width { get; set; }
		public int? Height { get; set; }

		public string TranscodedAssetPath { get; set; }

		public string BuildExternalRef()
		{
			switch (this.AssetType)
			{
				case AssetType.MasterCoverArt:
				case AssetType.MasterArtistArt:
				case AssetType.MasterLabelArt:
					{
						return String.Format("{0}/{1}", this.AssetType, this.MasterSha256Checksum).ToLower();
					}
				case AssetType.TrackProduct:
				case AssetType.TrackPreview:
					{
						var externalOwnerRef = Urn.Parse(this.ExternalOwnerUrn).IdValue;
						return String.Format("{0}/{1}/{2}/{3}/{4}/{5}", this.AssetType, this.MasterSha256Checksum, externalOwnerRef, this.FileExtension.ToLower(), this.BitRateKbps ?? 0, this.DurationMs ?? 0).ToLower();
					}
				default:
					{
						var message = String.Format("AssetType not supported: {0}-{1}-{2}", AssetType, FileExtension.ToLower(), MasterSha256Checksum);
						throw new Exception(message);
					}
			}
		}

		public AssetId CreateAssetId()
		{
			return new AssetId { AssetType = this.AssetType, ExternalUrn = this.ExternalUrn };
		}

		public override string ToString()
		{
			return this.ExternalRef;
		}
	}

	public class LabelUpdate : IExternal
	{
		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public DateTime? EstablishedDate { get; set; }

		public string Biography { get; set; }
		public string BiographyAuthor { get; set; }

		public List<AssetId> AssetIds { get; set; }

		public List<AssetId> GetImageAssetIds()
		{
			return AssetIds.Where(x => x.AssetType.IsImage()).ToList();
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.ExternalRef);
		}
	}

	public class ProductUpdate
	{
		public bool Delete { get; set; }

		public string TerritoryCode { get; set; }
		public string Copyright { get; set; }
		public string ReportRef { get; set; }

		public bool AllowDownload { get; set; }
		public bool AllowStreaming { get; set; }
		public bool AllowSubscription { get; set; }

		public bool CollectionOnly { get; set; }

		public DateTime? DownloadStartDate { get; set; }
		public DateTime? DownloadEndDate { get; set; }

		public List<CostPoint> CostPoints { get; set; }

		public DateTime? StreamingStartDate { get; set; }
		public DateTime? StreamingEndDate { get; set; }

		public override string ToString()
		{
			return this.TerritoryCode;
		}
	}

	public class ReleaseChangeSet
	{
		public Guid BatchId { get; set; }
		public string BatchName { get; set; }
		public long BatchSequence { get; set; }
		public string BatchPath { get; set; }

		public bool Partial { get; set; }

		public ReleaseUpdate Release { get; set; }

		public List<LabelUpdate> Labels { get; set; }
		public List<ArtistUpdate> Artists { get; set; }
		public List<TrackUpdate> Tracks { get; set; }
		public List<AssetUpdate> Assets { get; set; }

		public void UpdateBatchInfo(Batch batch)
		{
			this.BatchId = batch.Id;
			this.BatchName = batch.Name;
			this.BatchSequence = batch.SequenceNumber;
			this.BatchPath = batch.Path;
		}

		public override string ToString()
		{
			return String.Format("{0} {1} {2}", this.Release.ExternalUrn, this.BatchName, this.Partial ? "Partial" : "Full");
		}
	}

	public static class ReleaseChangeSetSerializer
	{
		public static string Serialize(ReleaseChangeSet changeSet)
		{
			return TypeSerializer.SerializeToString(changeSet);
		}

		public static ReleaseChangeSet Deserialize(string changeSetString)
		{
			return TypeSerializer.DeserializeFromString<ReleaseChangeSet>(changeSetString);
		}
	}

	public class TrackUpdate : IExternalDeletable
	{
		public bool Delete { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public string NameExVersion { get; set; }
		public string NameVersion { get; set; }

		public string LabelText { get; set; }
		public string ArtistText { get; set; }
		public string ReleaseText { get; set; }

		public string Isrc { get; set; }
		public string GlobalReleaseId { get; set; }

		public int? SetNumber { get; set; }
		public int? DiscNumber { get; set; }
		public int? TrackNumber { get; set; }
		public int? SequenceNumber { get; set; }

		public int? DurationMs { get; set; }

		public ExplicitType ExplicitType { get; set; }

		public string RightsHolder { get; set; }
		public string Copyright { get; set; }

		public List<string> Publishers { get; set; }

		public List<string> Genres { get; set; }
		public List<string> SubGenres { get; set; }

		public string Review { get; set; }
		public string ReviewAuthor { get; set; }

		public string Lyrics { get; set; }

		public List<Participant> Participants { get; set; }

		public string ExternalLabelUrn { get; set; }
		public string ExternalReleaseUrn { get; set; }

		public MergeList<string> ExternalArtistUrns { get; set; }
		public MergeList<ProductUpdate> Products { get; set; }

		public List<AssetId> AssetIds { get; set; }

		public void AddAssetIds(IEnumerable<AssetId> assetIds)
		{
			if (assetIds == null || !assetIds.Any())
			{
				return;
			}

			if (this.AssetIds == null)
			{
				this.AssetIds = new List<AssetId>();
			}

			this.AssetIds.AddRange(assetIds);
		}

		public List<AssetId> GetImageAssetIds()
		{
			return this.AssetIds.SafeWhere(x => x.AssetType.IsImage()).ToList();
		}

		public List<AssetId> GetAudioAssetIds()
		{
			return this.AssetIds.SafeWhere(x => x.AssetType.IsAudio()).ToList();
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.ExternalRef);
		}
	}

	public class ReleaseUpdate : IExternalDeletable
	{
		public const string WorldTerritoryCode = "ZZ";

		public bool Delete { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public string NameExVersion { get; set; }
		public string NameVersion { get; set; }

		public ReleaseType ReleaseType { get; set; }

		public string LabelText { get; set; }
		public string ArtistText { get; set; }

		public string UpcEan { get; set; }
		public string GlobalReleaseId { get; set; }
		public string CatalogueNumber { get; set; }

		public int? SetCount { get; set; }
		public int? DiscCount { get; set; }
		public int? TrackCount { get; set; }

		public int? DurationMs { get; set; }
		public bool ContinuousMix { get; set; }

		public ExplicitType ExplicitType { get; set; }

		public DateTime? ReleaseDate { get; set; }

		public string RightsHolder { get; set; }
		public string Copyright { get; set; }

		public List<string> Genres { get; set; }
		public List<string> SubGenres { get; set; }

		public string Review { get; set; }
		public string ReviewAuthor { get; set; }

		public List<Participant> Participants { get; set; }

		public string ExternalLabelUrn { get; set; }

		public MergeList<string> ExternalArtistUrns { get; set; }
		public MergeList<string> ExternalTrackUrns { get; set; }
		public MergeList<ProductUpdate> Products { get; set; }

		public List<AssetId> AssetIds { get; set; }

		public void AddAssetIds(IEnumerable<AssetId> assetIds)
		{
			if (assetIds == null || !assetIds.Any())
			{
				return;
			}

			if (this.AssetIds == null)
			{
				this.AssetIds = new List<AssetId>();
			}

			this.AssetIds.AddRange(assetIds);
		}

		public List<AssetId> GetImageAssetIds()
		{
			return this.AssetIds.SafeWhere(x => x.AssetType.IsImage()).ToList();
		}

		public List<AssetId> GetAudioAssetIds()
		{
			return this.AssetIds.SafeWhere(x => x.AssetType.IsAudio()).ToList();
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.ExternalRef);
		}
	}

	public class AssetId
	{
		public AssetType AssetType { get; set; }
		public string ExternalUrn { get; set; }

		public override string ToString()
		{
			return this.ExternalUrn;
		}
	}

	public class Artist : IContent
	{
		public Guid Id { get; set; }
		public string Urn { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public DateTime? BirthDate { get; set; }
		public string BirthPlace { get; set; }

		public DateTime? DeathDate { get; set; }
		public string DeathPlace { get; set; }

		public string DecadesActive { get; set; }

		public string Biography { get; set; }
		public string BiographyAuthor { get; set; }

		public List<Asset> Assets { get; set; }

		public DateTime CreatedDate { get; set; }
		public DateTime ModifiedDate { get; set; }

		public List<Asset> GetImageAssets()
		{
			return this.Assets.Where(x => x.AssetType.IsImage()).ToList();
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.Id.ToString("N"));
		}
	}

	public class Asset : IContent
	{
		public Guid Id { get; set; }
		public string Urn { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }

		public AssetType AssetType { get; set; }

		public string ExternalOwnerUrn { get; set; }

		public string FileName { get; set; }
		public string FileExtension { get; set; }
		public long FileSizeBytes { get; set; }

		public string MasterSha256Checksum { get; set; }
		public string Md5Checksum { get; set; }

		public int? DurationMs { get; set; }
		public int? BitRateKbps { get; set; }

		public int? Width { get; set; }
		public int? Height { get; set; }

		public string TranscodedAssetPath { get; set; }
		public string RepositoryAssetPath { get; set; }

		public DateTime CreatedDate { get; set; }
		public DateTime ModifiedDate { get; set; }

		public override string ToString()
		{
			return String.Format("{0} {1}", this.ExternalRef, this.Id.ToString("N"));
		}
	}

	public class Label : IContent
	{
		public Guid Id { get; set; }
		public string Urn { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public DateTime? EstablishedDate { get; set; }

		public string Biography { get; set; }
		public string BiographyAuthor { get; set; }

		public List<Asset> Assets { get; set; }

		public DateTime CreatedDate { get; set; }
		public DateTime ModifiedDate { get; set; }

		public List<Asset> GetImageAssets()
		{
			return this.Assets.Where(x => x.AssetType.IsImage()).ToList();
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.Id.ToString("N"));
		}
	}

	public class Product
	{
		public string TerritoryCode { get; set; }
		public string Copyright { get; set; }
		public string ReportRef { get; set; }

		public bool AllowDownload { get; set; }
		public bool AllowStreaming { get; set; }
		public bool AllowSubscription { get; set; }

		public bool CollectionOnly { get; set; }

		public DateTime? DownloadStartDate { get; set; }
		public DateTime? DownloadEndDate { get; set; }

		public List<CostPoint> CostPoints { get; set; }

		public DateTime? StreamingStartDate { get; set; }
		public DateTime? StreamingEndDate { get; set; }

		public override string ToString()
		{
			return this.TerritoryCode;
		}
	}


	public class Release : IContent
	{
		public const string WorldTerritoryCode = "ZZ";

		public Guid Id { get; set; }
		public string Urn { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public string NameExVersion { get; set; }
		public string NameVersion { get; set; }

		public ReleaseType ReleaseType { get; set; }

		public string LabelText { get; set; }
		public string ArtistText { get; set; }

		public string UpcEan { get; set; }
		public string GlobalReleaseId { get; set; }
		public string CatalogueNumber { get; set; }

		public int SetCount { get; set; }
		public int DiscCount { get; set; }
		public int TrackCount { get; set; }

		public int? DurationMs { get; set; }
		public bool ContinuousMix { get; set; }

		public ExplicitType ExplicitType { get; set; }

		public DateTime? ReleaseDate { get; set; }

		public string RightsHolder { get; set; }
		public string Copyright { get; set; }

		public List<string> Genres { get; set; }
		public List<string> SubGenres { get; set; }

		public string Review { get; set; }
		public string ReviewAuthor { get; set; }

		public List<Participant> Participants { get; set; }

		public Label Label { get; set; }
		public List<Artist> Artists { get; set; }
		public List<Track> Tracks { get; set; }
		public List<Product> Products { get; set; }
		public List<Asset> Assets { get; set; }

		public DateTime CreatedDate { get; set; }
		public DateTime ModifiedDate { get; set; }
		public DateTime? DeletedDate { get; set; }

		public string GetPrimaryGenre()
		{
			return this.Genres.IsNullOrEmpty() ? null : this.Genres[0];
		}

		public Artist GetPrimaryArtist()
		{
			return this.Artists.IsNullOrEmpty() ? null : this.Artists[0];
		}

		public List<Asset> GetImageAssets()
		{
			return this.Assets.Where(x => x.AssetType.IsImage()).ToList();
		}

		public List<Asset> GetAudioAssets()
		{
			return this.Assets.Where(x => x.AssetType.IsAudio()).ToList();
		}

		public List<Track> GetActiveTracks()
		{
			return this.Tracks.SafeWhere(x => !x.CollectionOrphan).ToList();
		}

		public List<Track> GetOrphanTracks()
		{
			return this.Tracks.SafeWhere(x => x.CollectionOrphan).ToList();
		}

		public Product GetProduct(string territoryCode)
		{
			if (this.Products.IsNullOrEmpty())
			{
				return null;
			}

			var product = this.Products.FirstOrDefault(x => x.TerritoryCode.EqualsIgnoreCase(territoryCode));
			if (product == null)
			{
				// Default to the world product if exists
				product = this.Products.FirstOrDefault(x => x.TerritoryCode.EqualsIgnoreCase(WorldTerritoryCode));
			}

			return product;
		}

		public string GetDescription()
		{
			return String.Format("{0} by {1}", this.Name, this.ArtistText);
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.Id.ToString("N"));
		}
	}

	public class Track : IContentDeletable
	{
		public Guid Id { get; set; }
		public string Urn { get; set; }

		public string ExternalRef { get; set; }
		public string ExternalUrn { get; set; }

		public string SupplierKeyName { get; set; }
		public string Name { get; set; }

		public string NameExVersion { get; set; }
		public string NameVersion { get; set; }

		public string LabelText { get; set; }
		public string ArtistText { get; set; }
		public string ReleaseText { get; set; }

		public string Isrc { get; set; }
		public string GlobalReleaseId { get; set; }

		public bool CollectionOrphan { get; set; }

		public int SetNumber { get; set; }
		public int DiscNumber { get; set; }
		public int TrackNumber { get; set; }
		public int SequenceNumber { get; set; }

		public int? DurationMs { get; set; }

		public ExplicitType ExplicitType { get; set; }

		public string RightsHolder { get; set; }
		public string Copyright { get; set; }

		public List<string> Publishers { get; set; }

		public List<string> Genres { get; set; }
		public List<string> SubGenres { get; set; }

		public string Review { get; set; }
		public string ReviewAuthor { get; set; }

		public string Lyrics { get; set; }

		public List<Participant> Participants { get; set; }

		public Label Label { get; set; }
		public Release Release { get; set; }
		public List<Artist> Artists { get; set; }
		public List<Product> Products { get; set; }
		public List<Asset> Assets { get; set; }

		public DateTime CreatedDate { get; set; }
		public DateTime ModifiedDate { get; set; }
		public DateTime? DeletedDate { get; set; }

		public string GetPrimaryGenre()
		{
			return this.Genres.IsNullOrEmpty() ? null : this.Genres[0];
		}

		public Artist GetPrimaryArtist()
		{
			return this.Artists.IsNullOrEmpty() ? null : this.Artists[0];
		}

		public List<Asset> GetImageAssetIds()
		{
			return this.Assets.Where(x => x.AssetType.IsImage()).ToList();
		}

		public List<Asset> GetAudioAssetIds()
		{
			return this.Assets.Where(x => x.AssetType.IsAudio()).ToList();
		}

		public Product GetProduct(string territoryCode)
		{
			if (this.Products.IsNullOrEmpty())
			{
				return null;
			}

			var product = this.Products.FirstOrDefault(x => x.TerritoryCode.EqualsIgnoreCase(territoryCode));
			if (product == null)
			{
				// Default to the world product if exists
				product = this.Products.FirstOrDefault(x => x.TerritoryCode.EqualsIgnoreCase(Release.WorldTerritoryCode));
			}

			return product;
		}

		public string GetDescription()
		{
			return String.Format("{0} {1} ({2}) by {3}", this.SequenceNumber, this.Name, this.ReleaseText, this.ArtistText);
		}

		public override string ToString()
		{
			return String.Format("{0} {1}", this.Name, this.Id.ToString("N"));
		}
	}

	public enum AssetType
	{
		None,
		MasterCoverArt,
		MasterArtistArt,
		MasterLabelArt,
		ResizedCoverArt,
		ResizedArtistArt,
		ResizedLabelArt,
		TrackProduct,
		TrackPreview,
	}

	public static class AssetTypeExtensions
	{
		public static bool IsAudio(this AssetType assetType)
		{
			return assetType == AssetType.TrackProduct || assetType == AssetType.TrackPreview;
		}

		public static bool IsMasterImage(this AssetType assetType)
		{
			return assetType == AssetType.MasterCoverArt || assetType == AssetType.MasterArtistArt || assetType == AssetType.MasterLabelArt;
		}

		public static bool IsResizedImage(this AssetType assetType)
		{
			return assetType == AssetType.ResizedCoverArt || assetType == AssetType.ResizedArtistArt || assetType == AssetType.ResizedLabelArt;
		}

		public static bool IsImage(this AssetType assetType)
		{
			switch (assetType)
			{
				case AssetType.MasterCoverArt:
				case AssetType.MasterArtistArt:
				case AssetType.MasterLabelArt:
				case AssetType.ResizedCoverArt:
				case AssetType.ResizedArtistArt:
				case AssetType.ResizedLabelArt:
					return true;
				default:
					return false;
			}
		}

		public static AssetType GetMasterType(this AssetType assetType)
		{
			switch (assetType)
			{
				case AssetType.ResizedCoverArt:
					return AssetType.MasterCoverArt;
				case AssetType.ResizedArtistArt:
					return AssetType.MasterArtistArt;
				case AssetType.ResizedLabelArt:
					return AssetType.MasterLabelArt;
				default:
					return assetType;
			}
		}

		public static AssetType GetResizedType(this AssetType assetType)
		{
			switch (assetType)
			{
				case AssetType.MasterCoverArt:
					return AssetType.ResizedCoverArt;
				case AssetType.MasterArtistArt:
					return AssetType.ResizedArtistArt;
				case AssetType.MasterLabelArt:
					return AssetType.ResizedLabelArt;
				default:
					return assetType;
			}
		}
	}

	public struct Urn
	{
		private const char IdValueSeperator = '/';
		private readonly string urnString;

		public string ResourceName
		{
			get;
			private set;
		}

		public string IdTypeName
		{
			get;
			private set;
		}

		public string IdValue
		{
			get;
			private set;
		}

		public string[] IdValues
		{
			get
			{
				return IdValue.Split(IdValueSeperator);
			}
		}

		public Urn(string resourceName, string idTypeName, string idValue)
			: this()
		{
			if (resourceName == null)
			{
				throw new ArgumentNullException("resourceName");
			}

			if (idValue == null)
			{
				throw new ArgumentNullException("idValue");
			}

			this.ResourceName = resourceName.ToLower();
			this.IdTypeName = !String.IsNullOrEmpty(idTypeName) ? idTypeName.ToLower() : null;
			this.IdValue = idValue;

			if (String.IsNullOrEmpty(this.IdTypeName))
			{
				this.urnString = string.Format("urn:{0}:{1}", this.ResourceName, this.IdValue);
			}
			else
			{
				this.urnString = string.Format("urn:{0}:{1}:{2}", this.ResourceName, this.IdTypeName, this.IdValue);
			}
		}

		public Urn(string resourceName, string idValue)
			: this(resourceName, null, idValue)
		{
		}

		public bool IsDefaultIdType()
		{
			return String.IsNullOrEmpty(IdTypeName);
		}

		public bool IsResourceType(string resourceName)
		{
			return string.Compare(this.ResourceName, resourceName, true) == 0;
		}

		public bool IsIdType(Type type)
		{
			return this.IsIdType(type.Name);
		}

		public bool IsIdType(string idTypeName)
		{
			return string.Compare(this.IdTypeName, idTypeName, true) == 0;
		}

		public override int GetHashCode()
		{
			return this.urnString.GetHashCode();
		}

		public bool Equals(Urn obj)
		{
			return String.CompareOrdinal(obj.urnString, this.urnString) == 0;
		}

		public override bool Equals(object obj)
		{
			return obj.GetType() == typeof(Urn) && Equals(obj);
		}

		public override string ToString()
		{
			return urnString;
		}

		// Operators overloading

		public static implicit operator string(Urn urn)
		{
			return urn.urnString;
		}

		public static bool operator ==(Urn urn1, Urn urn2)
		{
			return urn1.Equals(urn2);
		}

		public static bool operator !=(Urn urn1, Urn urn2)
		{
			return !urn1.Equals(urn2);
		}

		// Parsing

		public static bool IsValidUrn(string urnString)
		{
			var fields = urnString.Split(':');
			return (fields.Length == 3 || fields.Length == 4) && String.CompareOrdinal(fields[0], "urn") == 0;
		}

		public static bool TryParse(string urnString, out Urn urn)
		{
			var fields = urnString.Split(':');

			if ((fields.Length == 3 || fields.Length == 4) && String.CompareOrdinal(fields[0], "urn") == 0)
			{
				if (fields.Length == 4)
				{
					urn = new Urn(fields[1], fields[2], fields[3]);
				}
				else
				{
					urn = new Urn(fields[1], fields[2]);
				}

				return true;
			}

			urn = new Urn();
			return false;
		}

		public static Urn Parse(string urnText)
		{
			if (String.IsNullOrEmpty(urnText))
			{
				throw new ArgumentNullException("urnText");
			}

			var fields = urnText.Split(':');
			if ((fields.Length == 3 || fields.Length == 4) && String.CompareOrdinal(fields[0], "urn") == 0)
			{
				return fields.Length == 4 ? new Urn(fields[1], fields[2], fields[3]) : new Urn(fields[1], fields[2]);
			}

			var msg = string.Format("Invalid URN text '{0}'", urnText);
			throw new FormatException(msg);
		}

		public static string GetUrnType(string urnString)
		{
			var urn = Parse(urnString);
			return urn.ResourceName;
		}

		public static long GetLongId(string urnString)
		{
			var urn = Parse(urnString);
			return Convert.ToInt64(urn.IdValue);
		}

		public static Guid GetGuidId(string urnString)
		{
			var urn = Parse(urnString);
			return new Guid(urn.IdValue);
		}

		public static string[] GetIdValues(string urnString)
		{
			var urn = Parse(urnString);
			return urn.IdValues;
		}

		public static string GetIdValue(string urnString)
		{
			var urn = Parse(urnString);
			return urn.IdValue;
		}

		public static string CleanIdValue(string idValue)
		{
			return idValue.Trim().ToLowerInvariant().Replace(' ', '_');
		}

		public static string GetFirstIdValue(string urnString)
		{
			var urn = Parse(urnString);
			return urn.IdValue.Split('/')[0];
		}

		public static string GetSecondIdValue(string urnString)
		{
			var urn = Parse(urnString);
			var parts = urn.IdValue.Split('/');
			return parts.Length > 1 ? parts[1] : null;
		}
	}

}